package openapi

import (
	"crypto"
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"fmt"
	mrand "math/rand"
	"reflect"
	"strconv"
	"strings"
	"time"

	"gitee.com/bjf-fhe/apinx/errors"
	"github.com/Knetic/govaluate"
	"github.com/getkin/kin-openapi/openapi3"
)

var handling interface{} = nil

var NullValue = errors.New(10000, "null value")

//
//encodings 用于传入参数的编码方式，如果不为空，则使用编码方式进行编码
func GetStrValue(name string, typ string, contentType string,
	others InputGetter,
	// inputs map[string]interface{},
	handledMark *map[string]interface{},
	encodings map[string]*openapi3.Encoding,
) (string, error) {
	schema, val, err := GetValue(name, typ, contentType, others, handledMark)
	if err == nil {
		var style string
		var encoding *openapi3.Encoding
		if encodings != nil {
			encoding = encodings[name]
		}
		if encoding != nil {
			style = encoding.Style
		}
		return EncodingParToString(schema, style, val), nil
	}
	return "", err
}

type InputGetter interface {
	//返回某个类型下某个名称的输入，可以返回nil
	GetInput(typ string, name string) *ParameterSelection
	//获得某个类型的所有输入keys
	LoopInType(typ string, fn func(key string))
	//获得某个参数额定义
	GetSchema(typ, contentType string, name string) *openapi3.Schema
}

//GetValue 返回对应的参数的计算值，name 待计算参数的名称，用于比较和过滤 typ 用于标注其他来源的参数 others 传入的其他参数, inputs 用户传入的输入
//args 用于在迭代过程中标注已经处理过的参数
//def 传入的整体消息定义（注意：是整体的定义，以便在迭代过程中获取其他参数的定义）
//返回：
//1、具体处理后的值，使用interface{}以便兼容array、object、number和string等不同类型
func GetValue(name string, typ string, contentType string,
	others InputGetter,
	// inputs map[string]interface{},
	handled *map[string]interface{},
	// schemas openapi3.Schemas,
	// tempSecutirySchemas openapi3.Schemas,
) (*openapi3.Schema, interface{}, error) {
	//如果p是nil,则
	//1)从others中获取
	var p *ParameterSelection
	if others != nil {
		p = others.GetInput(typ, name)
	}
	//获得参数的schema
	schema := others.GetSchema(typ, contentType, name)

	if handled == nil {
		var m = make(map[string]interface{})
		handled = &m
	}

	var evalator string
	if schema != nil {
		if bjfCalculator, fromXbjf := schema.Extensions["x-bjf-calculator"]; fromXbjf {
			evalator = bjfCalculator.(string)
			if p != nil {
				p.Fixed = true
				p.Calculated = true
				(*handled)[name] = p.Value
			}
		} else {
			defaultStr, ok := schema.Default.(string)
			if ok {
				evalator = defaultStr
			}
		}
		if p == nil {
			var newP ParameterSelection
			//判断inputs中是否有同名的参数
			// if inputs != nil {
			// 	if _, ok := inputs[name]; !ok {
			newP.Fixed = true
			// }
			// }
			if evalator != "" {
				// newP.Fixed = true
				newP.Calculated = evalator[0] == '='
			}
			p = &newP
		}
	} else if p != nil {
		if p.Value != "" {
			if sPValue, ok := p.Value.(string); ok {
				if sPValue[0] == '=' {
					evalator = sPValue
				}
			}
		}
	} else {
		return nil, nil, errors.New(10000, "parameter "+name+" not found")
	}

	var value interface{}
	if p.Fixed {
		if p.Calculated {
			//计算公式改为从schema中获取，用于兼容公式中还包含自身值的情况

			var err error
			var exp *govaluate.EvaluableExpression

			if evalator != "" {
				if evalator[0] == '=' {
					evalator = evalator[1:]
				}
				//rsa256(join(asc(otherArgs()),"&"),privateKey)
				exp, err = govaluate.NewEvaluableExpressionWithFunctions(evalator, map[string]govaluate.ExpressionFunction{
					"otherArgs": func(arguments ...interface{}) (interface{}, error) {
						//TODO 返回其他所有参数
						var err error
						others.LoopInType(typ, func(k1 string) {
							// args[k1] = handling
							if k1 != name {
								_, _, err = GetValue(k1, typ, contentType, others, handled)
								if err != nil {
									delete((*handled), k1)
								}
							}
						})
						//去除迭代中的占位符
						args := make(map[string]interface{})
						for k, v := range *handled {
							if v != nil {
								args[k] = v
							}
						}
						return args, nil
					},
					"rand": func(arguments ...interface{}) (interface{}, error) {
						var rvalue int
						if len(arguments) == 0 {
							rvalue = mrand.Int()
						} else if len(arguments) == 1 {
							rvalue = mrand.Intn(int(arguments[0].(float64)))
						} else {
							rvalue = mrand.Intn(int(arguments[1].(float64)-arguments[0].(float64))) + int(arguments[0].(float64))
						}
						return strconv.Itoa(rvalue), nil
					},
					"asc": func(arguments ...interface{}) (interface{}, error) {
						return AscOrderedMap(arguments[0].(map[string]interface{})), nil
					},
					"desc": func(arguments ...interface{}) (interface{}, error) {
						return DescOrderedMap(arguments[0].(map[string]interface{})), nil
					},
					"md5": func(arguments ...interface{}) (interface{}, error) {
						var sum = md5.New()
						for _, v := range arguments {
							sum.Write([]byte(fmt.Sprintf("%v", v)))
						}
						return hex.EncodeToString(sum.Sum(nil)), nil
					},
					"join": func(arguments ...interface{}) (interface{}, error) {
						var strArr = make([]string, 0)
						if len(arguments) > 1 {
							for _, v := range arguments[:len(arguments)-1] {
								switch tv := v.(type) {
								case string:
									if tv != "" {
										strArr = append(strArr, tv)
									}
								case AscOrderedMap:
									strArr = append(strArr, tv.Strings()...)
								default:
									strArr = append(strArr, fmt.Sprintf("%v", v))
								}
							}
						}
						joiner, ok := arguments[len(arguments)-1].(string)
						if ok {
							fmt.Println(strings.Join(strArr, joiner))
							return strings.Join(strArr, joiner), nil
						}
						return nil, fmt.Errorf("不支持的Join参数类型：%T", arguments[1])
					},
					"notempty": func(arguments ...interface{}) (interface{}, error) {
						var strArr = make([]interface{}, 0)
						for _, v := range arguments {
							switch tv := v.(type) {
							case string:
								if tv != "" {
									strArr = append(strArr, tv)
								}
							default:
								strArr = append(strArr, v)
							}
						}
						return strArr, nil
					},
					"timestamp": func(arguments ...interface{}) (interface{}, error) {
						var now = time.Now()
						return strconv.FormatInt(now.Unix(), 10), nil
					},
					"timestamp_milli": func(arguments ...interface{}) (interface{}, error) {
						var now = time.Now()
						return strconv.FormatInt(now.UnixMilli(), 10), nil
					},
					"to_upper": func(arguments ...interface{}) (interface{}, error) {
						var result = make([]string, 0)
						for _, v := range arguments {
							switch tv := v.(type) {
							case string:
								result = append(result, strings.ToUpper(tv))
							}
						}
						return result, nil
					},
					"to_lower": func(arguments ...interface{}) (interface{}, error) {
						var result = make([]string, 0)
						for _, v := range arguments {
							switch tv := v.(type) {
							case string:
								result = append(result, strings.ToLower(tv))
							}
						}
						return result, nil
					},
					"sub_str": func(arguments ...interface{}) (interface{}, error) {
						var str = toStr(arguments[0])
						var start = toInt(arguments[1])
						var end = toInt(arguments[2])
						return str[start:end], nil
					},
					"base64": func(arguments ...interface{}) (interface{}, error) {
						switch tv := arguments[0].(type) {
						case string:
							return base64.StdEncoding.EncodeToString([]byte(tv)), nil
						case []byte:
							return base64.StdEncoding.EncodeToString(tv), nil
						default:
							var str = toStr(arguments[0])
							return base64.StdEncoding.EncodeToString([]byte(str)), nil
						}
					},
					"sha256": func(arguments ...interface{}) (interface{}, error) {
						sum := sha256.New()
						for _, v := range arguments[:len(arguments)-1] {
							sum.Write([]byte(fmt.Sprintf("%v", v)))
						}
						return sum.Sum(nil), nil
					},
					"hmac_sha256": func(arguments ...interface{}) (interface{}, error) {
						var key = toStr(arguments[1])
						var data = toStr(arguments[0])
						mac := hmac.New(sha256.New, []byte(key))
						mac.Write([]byte(data))
						return mac.Sum(nil), nil
					},
					"rsa256": func(arguments ...interface{}) (interface{}, error) {
						sum := sha256.New()
						for _, v := range arguments[:len(arguments)-1] {
							fmt.Printf("%T,%v", v, v)
							sum.Write([]byte(fmt.Sprintf("%v", v)))
						}
						d := sum.Sum(nil)

						block, rest := pem.Decode([]byte(arguments[len(arguments)-1].(string)))
						fmt.Println(string(rest))
						if block == nil {
							return "", errors.New(10000, "私钥错误")
						}
						parseResult, err := x509.ParsePKCS8PrivateKey(block.Bytes)
						var privateKey *rsa.PrivateKey
						if err != nil {
							if strings.Contains(err.Error(), "use ParsePKCS1PrivateKey instead for this key format") {
								privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
								if err != nil {
									return "", err
								}
							} else {
								return "", err
							}
						} else {
							privateKey = parseResult.(*rsa.PrivateKey)
						}

						signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, d)
						if err != nil {
							panic(err)
						}
						encodedSig := base64.StdEncoding.EncodeToString(signature)
						return encodedSig, nil
					},
				})
			} else {
				err = fmt.Errorf("表达式为空")
			}
			if err == nil {

				for _, token := range exp.Vars() {
					if token != name {
						if _, ok := (*handled)[token]; ok {
							continue
						}
						(*handled)[token] = handling
						_, (*handled)[token], err = GetValue(token, typ, contentType, others, handled)
						if err != nil {
							delete((*handled), token)
							return nil, "", err
						}
					}
				}
				// for k, v := range others {
				// 	for k1, v1 := range v {
				// 		var prefix = ""
				// 		if _, ok := handled[prefix+k1]; ok {
				// 			prefix = k + "_"
				// 		}
				// 		handled[prefix+k1] = handling
				// 		if k1 != name {
				// 			_, handled[prefix+k1], err = getValue(v1, k1, k, others, inputs, handled, schemas, tempSecutirySchemas)
				// 			if err != nil {
				// 				delete(handled, prefix+k1)
				// 			}
				// 		}
				// 	}
				// }
				value, err = exp.Evaluate((*handled))
				// if err == nil {
				// 	value =
				// 	return fmt.Sprintf("%v", result), nil
				// }
				(*handled)[name] = value
			}
			if err != nil {
				return nil, "", errors.New(10000, err.Error()+",name:"+name+",evalator:"+evalator)
			}
		} else {
			value = p.Value
		}
	} else {
		value = p.Value
		(*handled)[name] = value
	}

	if value == nil {
		return nil, "", NullValue
	}
	return schema, value, nil
}

func toStr(v interface{}) string {
	switch tv := v.(type) {
	case []string:
		return tv[0]
	case string:
		return tv
	default:
		return fmt.Sprintf("%v", v)
	}
}

func toInt(v interface{}) int64 {
	switch tv := v.(type) {
	case string:
		res, _ := strconv.ParseInt(tv, 10, 64)
		return res
	case int8, int16, int32, int64, int:
		return reflect.ValueOf(v).Int()
	case uint8, uint16, uint32, uint64, uint:
		val := reflect.ValueOf(v).Uint()
		return int64(val)
	case float64, float32:
		val := reflect.ValueOf(v).Float()
		return int64(val)
	default:
		return 0
	}
}
